﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.IO;
using Microsoft.ComplexEventProcessing;
using Microsoft.ComplexEventProcessing.Adapters;

namespace ProIAdapters.Input.SqlTrace
{
    /// <summary>
    /// Input adapter for Sql Trace
    /// 
    /// 
    /// 
    /// </summary>
    public class SqlTraceInput : TypedPointInputAdapter<SqlTraceEvent>
    {
        private readonly static IFormatProvider DateFormatProvider = CultureInfo.GetCultureInfo("en-us").DateTimeFormat;
        private const string DateFormatString = "ddd MMM dd HH:mm:ss yyyy";
        private PointEvent<SqlTraceEvent> pendingEvent;
        private SqlTraceConfig _config;
        const int streamBufferSize =  10000;
        const int dataArraySize = 10000;


        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="config">Configuration for this adapter</param>
        public SqlTraceInput(SqlTraceConfig config)
        {
            _config = config;
        }

        public override void Start()
        {
            ProduceEvents();
        }

        public override void Resume()
        {
            ProduceEvents();
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }


        /// <summary>
        /// Main loop
        /// </summary>
        private void ProduceEvents()
        {
            
                var currEvent = default(PointEvent<SqlTraceEvent>);
                // Loop until stop signal
                while (AdapterState != AdapterState.Stopping)
                {

                    try
                    {
                        Socket clientSocket = new Socket(AddressFamily.InterNetwork,
                        SocketType.Stream, ProtocolType.Tcp);
                        clientSocket.Connect(new IPEndPoint(
                        Dns.GetHostAddresses(_config.ServerName)[0], _config.Port));
                        Console.WriteLine("Client is connected.\n");
                        // Create a NetworkStream that owns clientSocket and
                        // then create a BufferedStream on top of the NetworkStream.
                        // Both streams are disposed when execution exits the
                        // using statement.
                        using (Stream
                             netStream = new NetworkStream(clientSocket, true),
                             bufStream =
                                   new BufferedStream(netStream, streamBufferSize))
                        {
                            // Check whether the underlying stream supports seeking.
                            Console.WriteLine("NetworkStream {0} seeking.\n",
                                bufStream.CanSeek ? "supports" : "does not support");
                            if (bufStream.CanRead)
                            {
                                DateTime startTime;
                                double networkTime, bufferedTime = 0;
                                int bytesReceived = 0;
                                byte[] receivedData = new byte[dataArraySize];
                                Console.WriteLine("Receiving data using BufferedStream.");
                                bytesReceived = 0;
                                startTime = DateTime.Now;
                                int numBytesToRead = receivedData.Length;
                            }

                            using (var streamReader = new StreamReader(netStream))
                            {
                                EnqueueCtiEvent(DateTimeOffset.Now);
                                try
                                {
                                    if (pendingEvent != null)
                                    {
                                        currEvent = pendingEvent;
                                        pendingEvent = null;
                                    }
                                    else
                                    {
                                        // Read from source
                                        //streamReader.
                                        while (!streamReader.EndOfStream)
                                        {
                                            //Console.Write(streamReader.ReadToEnd());
                                            var line = streamReader.ReadLine();
                                            //Console.WriteLine(line);
                                            if (line == null)
                                                continue;
                                            // Parse
                                            string[] fields = line.Split('|');
                                            if (fields.Length < 10)
                                                continue;
                                            var logevent = new SqlTraceEvent();
                                            logevent.EventClass = fields[0];

                                            logevent.ApplicationName = fields[1];
                                            logevent.DatabaseName = fields[2];
                                            logevent.NTUserName = fields[3];
                                            logevent.LoginName = fields[4];
                                            logevent.HostName = fields[5];
                                            logevent.ClientProcessId = fields[6];
                                            logevent.SPID = fields[7];
                                            logevent.StartTime = DateTime.Parse(fields[8]);
                                            logevent.IsSystem = fields[9];
                                            if (logevent.EventClass == "Audit Login")
                                            {
                                                logevent.TextData = fields[10];
                                            }
                                            else if (logevent.EventClass == "Audit Logout")
                                            {
                                                logevent.EndTime = DateTime.Parse(fields[10]);
                                            }
                                            else
                                            {
                                                logevent.EndTime = DateTime.Parse(fields[10]);
                                                logevent.TextData = fields[11];
                                            }

                                            
                                            // Produce INSERT event
                                            currEvent = CreateInsertEvent();
                                            currEvent.StartTime = DateTimeOffset.Now;
                                            currEvent.Payload = logevent;
                                            pendingEvent = null;
                                            //PrintEvent(currEvent);
                                            Enqueue(ref currEvent);
                                            // Also send a CTI event
                                            EnqueueCtiEvent(DateTimeOffset.Now);
                                        }

                                    }
                                    if (pendingEvent != null)
                                    {
                                        currEvent = pendingEvent;
                                        pendingEvent = null;
                                    }



                                }
                                catch (Exception e)
                                {
                                    Console.WriteLine(this.GetType().ToString() + ".ProduceEvents - " + e.Message + e.StackTrace);
                                }



                            }// stream reader scope 
                            // When bufStream is closed, netStream is in turn closed, which in turn 
                            // shuts down the connection and closes clientSocket.
                            Console.WriteLine("\nShutting down the connection.");
                            bufStream.Close();

                        } // end of bufstream scope
                        
                    } // end of try block
                    catch (System.Net.Sockets.SocketException e)
                    {
                        Console.WriteLine("No server at the other end");
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(this.GetType().ToString() + ".ProduceEvents - " + e.Message + e.StackTrace);
                    }
                    finally
                    {
                        System.Threading.Thread.Sleep(10000);
                    }
                }
                PrepareToStop(currEvent);
                Stopped();
        } //method close



        private string GetLog()
        {
            string result = "";


            return result;
        }


        private void PrepareToStop(PointEvent<SqlTraceEvent> currEvent)
        {
            //EnqueueCtiEvent(DateTime.Now);
            if (currEvent != null)
            {
                // Do this to avoid memory leaks
                ReleaseEvent(ref currEvent);
            }
        }

        private void PrepareToResume(PointEvent<SqlTraceEvent> currEvent)
        {
            pendingEvent = currEvent;
        }


        /// <summary>
        /// Debugging function
        /// </summary>
        /// <param name="evt"></param>
        private void PrintEvent(PointEvent<SqlTraceEvent> evt)
        {
        }


    }
}
